package com.yeskery.nut.script;

import com.yeskery.nut.core.*;
import com.yeskery.nut.http.BasicCookie;
import com.yeskery.nut.script.function.DefaultFunctionContext;
import com.yeskery.nut.script.function.Function;
import com.yeskery.nut.script.function.FunctionContext;
import com.yeskery.nut.script.function.FunctionExecutor;
import com.yeskery.nut.script.function.web.*;
import com.yeskery.nut.script.parser.ResponseMetadata;
import com.yeskery.nut.util.MediaTypeUtils;
import com.yeskery.nut.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;

/**
 * controller动态代理处理对象
 * @author sprout
 * 2022-05-14 14:55
 */
public class ScriptControllerInvocationHandler extends AbstractControllerInvocationHandler {

    /** 换行符 */
    private static final char NEW_LINE_SEPARATOR = '\n';
    /** 方法元数据 */
    private final Map<Method, Metadata> metadataMap;
    /** 函数执行器 */
    private final FunctionExecutor functionExecutor;

    /**
     * 构建controller动态代理处理对象
     * @param metadataMap 方法元数据
     * @param functionExecutor 函数执行器
     * @param target 目标对象
     */
    public ScriptControllerInvocationHandler(Map<Method, Metadata> metadataMap, FunctionExecutor functionExecutor, Object target) {
        super(target);
        this.metadataMap = metadataMap;
        this.functionExecutor = functionExecutor;
    }

    @Override
    protected void doRequest(Request request, Response response, Execution execution) {
        // 初始化WEB环境函数上下文
        FunctionContext webFunctionContext = initializeWebEnvFunctionContext(request, response);

        Metadata metadata = metadataMap.get(request.getMethod());

        // 处理响应cookie
        if (metadata.getResponseCookies() != null && !metadata.getResponseCookies().isEmpty()) {
            for (NameAndValue nameAndValue : metadata.getResponseCookies()) {
                String value = nameAndValue.getValue();
                if (!StringUtils.isEmpty(value) && value.contains(Function.FUNCTION_SEPARATOR)) {
                    value = functionExecutor.execute(value, webFunctionContext);
                }
                response.addCookie(new BasicCookie(nameAndValue.getKey(), value));
            }
        }

        // 处理响应头
        if (metadata.getResponseHeaders() != null && !metadata.getResponseHeaders().isEmpty()) {
            for (NameAndValue nameAndValue : metadata.getResponseHeaders()) {
                String value = nameAndValue.getValue();
                if (!StringUtils.isEmpty(value) && value.contains(Function.FUNCTION_SEPARATOR)) {
                    value = functionExecutor.execute(value, webFunctionContext);
                }
                response.addHeader(new NameAndValue(nameAndValue.getKey(), value));
            }
        }

        // 处理响应
        if (MediaTypeUtils.isBinaryMediaType(metadata.getMediaType())) {
            if (metadata instanceof ResponseMetadata) {
                response.write(((ResponseMetadata) metadata).getResponse(), metadata.getMediaType());
            } else {
                response.write(metadata.getBody(), metadata.getMediaType());
            }
        } else {
            String responseBody = metadata.getBody();
            StringBuilder stringBuilder = new StringBuilder();
            for (String line : responseBody.split(String.valueOf(NEW_LINE_SEPARATOR))) {
                if (StringUtils.isEmpty(line)) {
                    stringBuilder.append(line).append(NEW_LINE_SEPARATOR);
                } else {
                    stringBuilder.append(functionExecutor.execute(line, webFunctionContext)).append(NEW_LINE_SEPARATOR);
                }
            }
            response.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8), metadata.getMediaType());
        }
    }

    @Override
    protected boolean isTargetRequest(Request request, com.yeskery.nut.core.Method method) {
        Set<Method> methods = metadataMap.keySet();
        return methods.contains(com.yeskery.nut.core.Method.ALL) || methods.contains(method);
    }

    /**
     * 初始化web环境的函数
     * @param request 请求对象
     * @param response 响应对象
     */
    private FunctionContext initializeWebEnvFunctionContext(Request request, Response response) {
        FunctionContext functionContext = new DefaultFunctionContext();
        functionContext.registerFunction(new CookieFunction(request));
        functionContext.registerFunction(new HeaderFunction(request));
        functionContext.registerFunction(new PathFunction(request));
        functionContext.registerFunction(new MethodFunction(request));
        functionContext.registerFunction(new ParameterFunction(request));
        functionContext.registerFunction(new RequestBodyFunction(request));
        functionContext.registerFunction(new PathVariableFunction(request));
        functionContext.registerFunction(new ResponseStatusFunction(response));
        return functionContext;
    }
}
